﻿using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Text;
using Microsoft.CSharp;
using System.IO;

namespace AutoUnitTest.Common.CodeGeneration.CS
{
    public  class ClassGenerator : IClassGenerator
    {
        private StringWriter _sWriter;
        private bool _generateNamespace;

        protected CodeNamespace Namespace;
        protected CodeTypeDeclaration Class;
        protected ICodeGenerator CodeGenerator;
        protected List<CodeMemberMethod> Methods;

        public ClassGenerator()
        {
            _sWriter = new StringWriter(new StringBuilder());

            var options = new Dictionary<string, string> { { "CompilerVersion", "v4.0" } };

            CodeGenerator = new CSharpCodeProvider(options).CreateGenerator();
        }

        public virtual void CreateClass(string className, string nameSpace = null)
        {
            Class = new CodeTypeDeclaration(className)
                            {
                                Attributes = MemberAttributes.Public,
                                IsClass = true
                            };
            Methods = new List<CodeMemberMethod>();
            _generateNamespace = !String.IsNullOrEmpty(nameSpace);
            if (_generateNamespace)
            {
                Namespace = new CodeNamespace(nameSpace);
                Namespace.Types.Add(Class);
            }
        }

        public virtual void AddMethod(string methodName, 
            MethodParameter returnValue = null,
            MemberAttributes methodAccess = MemberAttributes.Assembly | MemberAttributes.Final,
            params MethodParameter[] methodParameters)
        {
            var method = new CodeMemberMethod {Name = methodName, Attributes = methodAccess}; 
            Methods.Add(method);

            if(methodParameters != null)
                if(methodParameters.Length > 0)
                    foreach (var methodParameter in methodParameters)
                    {
                        if(methodParameter.Type != null)
                            method.Parameters.Add(new CodeParameterDeclarationExpression(methodParameter.Type,
                                                                                     methodParameter.Name));
                        else method.Parameters.Add(
                            new CodeParameterDeclarationExpression(methodParameter.TypeAsString,
                                                                                     methodParameter.Name));
                    }

            if(returnValue != null)
            {
                method.ReturnType = new CodeTypeReference(returnValue.Type);

                if (!returnValue.Type.IsPrimitive && returnValue.Type != typeof(void))
                    method.Statements.Add(new CodeMethodReturnStatement(
                                              new CodeObjectCreateExpression(returnValue.Type)));
                else if (returnValue.Type.IsPrimitive)
                {
                    object defVal = Activator.CreateInstance(returnValue.Type);
                    method.Statements.Add(new CodeMethodReturnStatement(
                                              new CodePrimitiveExpression(defVal)));
                }
            }
            Class.Members.Add(method);
        }

        public virtual string GetCodeResult()
        {
            var codeGeneratorOptions =
                new CodeGeneratorOptions {BracingStyle = "C", BlankLinesBetweenMembers = false};
            if(_generateNamespace)
                CodeGenerator.GenerateCodeFromNamespace(Namespace,_sWriter, codeGeneratorOptions);
            else
                CodeGenerator.GenerateCodeFromType(Class, _sWriter, codeGeneratorOptions);

            return _sWriter.ToString();
        }
    }
}
